package com.azzcs.executor;

import com.azzcs.parse.BoundSql;
import com.azzcs.parse.Configuration;
import com.azzcs.parse.MappedStatement;
import com.azzcs.utils.GenericTokenParser;
import com.azzcs.utils.ParameterMapping;
import com.azzcs.utils.ParameterMappingTokenHandler;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: wzg
 * @Date: 2020/12/6 下午12:17
 */
public class SimpleExecutor implements Executor {

    public SimpleExecutor() {
    }

    public <T> List<T> query(Configuration configuration, MappedStatement mappedStatement, Object[] parameter) throws Exception {
        PreparedStatement preparedStatement = preparedStatement(configuration,mappedStatement,parameter);
        preparedStatement.executeQuery();
        return handleResult(mappedStatement,preparedStatement);
    }

    private <T> List<T> handleResult(MappedStatement mappedStatement, PreparedStatement preparedStatement) throws Exception {
        ResultSet resultSet = preparedStatement.getResultSet();
        List resultList = new ArrayList<>();
        Class<?> resultType = mappedStatement.getResultType();
        while (resultSet.next()){
            Object result = resultType.newInstance();
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);
                Object value = resultSet.getObject(columnName);
                Field field = resultType.getDeclaredField(columnName);
                field.setAccessible(true);
                field.set(result,value);
            }
            resultList.add(result);
        }

        return (List<T>)resultList;
    }

    private BoundSql getBoundSql(String sql) {
        ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", handler);
        String parse = genericTokenParser.parse(sql);
        List<ParameterMapping> parameterMappings = handler.getParameterMappings();
        return new BoundSql(parse,parameterMappings);
    }

    @Override
    public int update(Configuration configuration, MappedStatement mappedStatement, Object[] parameter) throws Exception {
        PreparedStatement preparedStatement = preparedStatement(configuration,mappedStatement,parameter);
        preparedStatement.execute();
        return preparedStatement.getUpdateCount();
    }

    private PreparedStatement preparedStatement(Configuration configuration, MappedStatement mappedStatement, Object[] parameter) throws Exception {
        Connection connection = configuration.getDataSource().getConnection();

        BoundSql boundSql =  getBoundSql(mappedStatement.getSql());
        String sql = boundSql.getSqlText();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        Class<?> parameterType = mappedStatement.getParameterType();
        for (int i = 1; i <= parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i - 1);
            String content = parameterMapping.getContent();
            Field field = parameterType.getDeclaredField(content);
            field.setAccessible(true);
            Object paramValue = field.get(parameter[0]);
            preparedStatement.setObject(i,paramValue);
        }
        return preparedStatement;
    }


}
